#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#include "calculator.h"

namespace
{
   namespace qi = boost::spirit::qi;
   namespace ascii = qi::ascii;

   template <typename Iterator>
   struct calculator : qi::grammar<Iterator, big_int(), ascii::space_type>
   {  
      calculator() : calculator::base_type(start)
      {
         using qi::_1;
         using qi::_2;
         using qi::_val;
         using qi::lit;
         //using namespace functions;

         big_int_str = +(ascii::digit);
         operation = +(ascii::alpha);
         
         number = big_int_str[_val = big_int_from_string()(_1)];
         
         base_expr = number[_val = _1] 
            | (lit("-") >> base_expr)[_val = -_1] 
            | (lit("(") >> expression >> lit(")"))[_val = _1]
            | (operation >> lit("(") >> expression >> lit(")"))[_val = user_function()(_1, _2)];
         
         power = base_expr[_val = _1] >> -(lit("^") >> power)[_val = pow()(_val, _1)];
         
         product = power[_val = _1] >> * ((lit("*") >> power)[_val *= _1] | (lit("/") >> power)[_val /= _1]);
         
         expression = product[_val = _1] >> * ((lit("+") >> product)[_val += _1] | (lit("-") >> product)[_val -= _1]);
         
         start %= expression > qi::eoi;
      }

   private:
      qi::rule<Iterator, big_int(), ascii::space_type> expression, product, power, number, base_expr, start;
      qi::rule<Iterator, std::string(), ascii::space_type> operation, big_int_str;
   };

   calculator<std::string::const_iterator> get_grammar;
}

std::pair<big_int, bool> calculate(const std::string& s)
{
   big_int result;
   bool flag = qi::phrase_parse(s.begin(), s.end(), get_grammar, ascii::space, result);
   return std::make_pair(result, flag);
}


